package plateau;

import InterfaceGraphique.MainApplication;
import cartes.Carte;
import cartes.Classe;
import cartes.Monstre;
import cartes.Objet;
import cartes.Race;
import common.Constantes;
import comportement.IncidentFacheux;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import plateau.exceptions.DefausserClasseRaceException;
import plateau.exceptions.EquiperException;


public class Joueur implements Serializable{
    
    /*
     * Attributs
     */
    private String nom;
    private int niveau;
    private Inventaire inventaire;
    private ArrayList<Carte> main;
    private int sexe;
    private boolean sexeChange = false;
    private int bonusCombat;
    private boolean estmort;

    public boolean isEstmort() {
        return estmort;
    }

    public void setEstmort(boolean estmort) {
        this.estmort = estmort;
    }
    
    
    
    
    
    
    public ArrayList<Carte> getMain() {
        return main;
    }
    
    
    //gérer les nains qui peuvent avoir 6 cartes en main.
    public void setMain(ArrayList<Carte> main) {
        this.main = main;
    }
    
    
    public void setSexe(int sexe) {
        this.sexe = sexe;
    }
    
    public void changeSexe() {
        this.sexeChange = !this.sexeChange;
    }
    
    private int bonusDeguerpir;
    private int actionUtiliser = 0;
    private int deguerpir = 0;
    
    public int getActionUtiliser() {
        return actionUtiliser;
    }
    
    public void setActionUtiliser(int actionUtiliser) {
        this.actionUtiliser = actionUtiliser;
    }
    
    public int getBonusDeguerpir() {
        return bonusDeguerpir;
    }
    
    public void setBonusDeguerpir(int bonusDeguerpir) {
        this.bonusDeguerpir = bonusDeguerpir;
    }
    
    public int getDeguerpir() {
        return deguerpir;
    }
    
    public void setDeguerpir(int deguerpir) {
        this.deguerpir = deguerpir;
    }
    
    
    /*
     * Constructeur
     */
    public Joueur(String nom, int sexe ) {
        this.nom = nom;
        this.niveau = 1;
        this.inventaire = new Inventaire();
        this.main = new ArrayList<Carte>();
        this.sexe = sexe;
        this.estmort = false;
    }
    public Joueur(String nom, int niveau, Inventaire inventaire, ArrayList<Carte> main, int sexe ) {
        this.nom = nom;
        this.niveau = niveau;
        this.inventaire = inventaire;
        this.main = main;
        this.sexe = sexe;
        this.estmort = false;
    }
    
    /*
     * Méthodes
     */
    public void aider() {
        
        
        
        
        if (this.inventaire.getRace().contains(Constantes.ELFE) && (this.getNiveau()!= 9))
            this.upNiveau(1, Boolean.TRUE);
    }
    
    
    
    /*
     * Utiliser une carte action sur un joueur
     * @param :
     *  - carte : la carte action à utiliser
     *  - joueur : le joueur sur lequel envoyer l'action ou null si l'action se fait sur lui même
     * @author : Arthur
     * @lastChange : Ophélie: action voleur
     */
    
    public void infligerAction(Carte c, Joueur j) {
        if( c.getActionCarte() == null )
            ; // pas une carte action, exception
        if( j == null )
            this.infligerAction(c);
        c.getActionCarte().utiliser(this, j);
        
        //voleur pendant le combat d'un autre joueur ( poignarder un joueur )
        if (this.inventaire.getClasse().contains(Constantes.VOLEUR)){
            if (this.actionUtiliser == 0){
                this.inventaire.getCartesClasse().get(0).getActionClasse().get(0).action(this, null, j, c);
                this.actionUtiliser++;
            }
            else{
                System.out.println("Vous avez déjà poignardé quelqu'un. Vous pourrez utiliser votre action au prochain combat");
                
            }
        }
        
        
        
        
    }
    
    /*
     * Utiliser une carte action sur soi même
     * @param :
     *  - carte : la carte action à utiliser
     * @author : Arthur
     */
    public void infligerAction(Carte c) {
        if( c.getActionCarte() == null )
            ; // pas une carte action, exception
        c.getActionCarte().utiliser(this);
    }
    
    /*
     * Utiliser une carte action sur un monstre
     * @param :
     *  - carte : la carte action à utiliser
     * @author : Arthur
     */
    public void infligerAction(Carte c, Monstre m) {
        if( c.getActionCarte() == null )
            ; // pas une carte action, exception
        c.getActionCarte().utiliser(m);
        
        
    }
    
    
    /*
     * Pioche une carte de la Partie dans la catégorie selectionnée
     * @param : La catégorie de la carte demandée (cf Constantes)
     * @author : Arthur
     */
    public Carte piocher(int categorie) {
        return Partie.piocher(categorie);
    }
    
    /*
     * Enlève une carte de la main pour l'ajouter à la défausse
     * @param : La carte à défausser
     * @author : Arthur
     */
    public void defausserCarteMain(Carte c) {
        Partie.getDefausse().add(c);
        main.remove(c);
    }
    
    public void defausserCarteMain(ArrayList<Carte> cartes) {
        ArrayList<Carte> list = new ArrayList<Carte>(cartes);
        for ( Carte c : list ) {
            Partie.getDefausse().add(c);
            main.remove(c);
        }        
    }
    
    
    
    /*
     * Enlève une carte de l'inventaire pour l'ajouter à la défausse
     * @param : La carte à défausser
     * @author : Arthur
     */
    public void defausserInventaire(Carte c) throws DefausserClasseRaceException {
        if( c != null ) {
            if( c.getClass().getName().equals(Constantes.C_CLASSE) ) {
                if( ! this.inventaire.isDefausserClasseOk((Classe) c) )
                    throw new DefausserClasseRaceException("Vous devez vous séparé des objets liés à cette classe avant. Et oui, c'est triste.");
            }
            
            if( c.getClass().getName().equals(Constantes.C_RACE) ) {
                if( ! this.inventaire.isDefausserRaceOk((Race) c) )
                    throw new DefausserClasseRaceException("Vous devez vous séparé des objets liés à cette race avant. Et oui, c'est triste.");
            }
            
            Partie.getDefausse().add(c);
            inventaire.remove(c);
        }
    }
    
    public void defausserInventaireForce(Carte c) {
        if( c != null ) {
                Partie.getDefausse().add(c);
                inventaire.remove(c);
        }
    }
    
    public void defausserInventaire(ArrayList<Carte> cartes) {
        
        if( cartes != null ) {
            for ( Carte c : cartes ) {               
                Partie.getDefausse().add(c);
                inventaire.remove(c);
            }
        }
    }
    
    public void chercherBagarre() {
    }
    
    /*
     * Mort d'un joueur : défausse de l'inventaire et de la main
     * @author : Arthur
     */
    public void mourir() {
       this.niveau=0;
        
        // Défausse de l'inventaire
        Partie.getDefausse().addAll(this.inventaire.vider());
        /*
         * Charité à implémenter
         * En attendant, défausse
         */
        
        // Défausse de la main du joueur
        Partie.getDefausse().addAll(this.main);
        this.main.clear();
    }
    
    /*
     * Equipe une carte dans l'inventaire
     * @param : Carte à ajouter
     * @author : Arthur
     * @lastChange : Ophélie: nain
     */
    public void equiper(Carte c) throws EquiperException {
        
        // L'objet est équipable
        if( c.equipable() ) {
            // C'est un objet
            if (c.getClass().getName().equals(Constantes.C_OBJET)) {
                Objet o = (Objet) c;
                
                // La classe correspond
                if (((o.getClasse() == Constantes.UNDEFINED || inventaire.getClasse().contains(o.getClasse())))
                        && !inventaire.getClasse().contains(o.getClasseInterdite())) {
                
                    
                    
                    // La race correspond
                    if (((o.getRace() == Constantes.UNDEFINED || inventaire.getRace().contains(o.getRace())))
                            && !inventaire.getRace().contains(o.getRaceInterdite())) {
                        
                        // Le sexe correspond
                        if ((o.getSexe() == Constantes.UNDEFINED || o.getSexe() == this.getSexe())) {
                            
                            // On vérfie qu'il n'a pas déjà de gros objet si c'en est un (ou < 2 pour le nain)
                            int nbGrosDispo = (this.inventaire.getRace().contains(Constantes.NAIN)) ? 2 : 1;
                            if (o.getGros() == false || this.inventaire.getNbGros() < nbGrosDispo ) {
                                
                                // Le type d'objet n'est pas déjà équipé
                                if (this.inventaire.isAvailable(o.getType())) {
                                    inventaire.add(c);
                                }
                                else
                                    throw new EquiperException("T'es déjà couvert de ce côté là !");
                            }
                            else
                                throw new EquiperException("Vous ne pouvez pas porter plus de gros objet.");
                        }
                        else
                            throw new EquiperException("Votre sexe ne permet pas d'équiper cet objet.");
                    }
                    else
                        throw new EquiperException("Votre race ne permet pas d'équiper cet objet.");
                }
                else
                    throw new EquiperException("Votre classe ne permet pas d'équiper cet objet.");
            }
            
            /*
             * La carte est une race
             */
            else if(c.getClass().getName().equals(Constantes.C_RACE)) {
                
                if( this.inventaire.getRace().contains(Constantes.HUMAIN)
                        || ( this.inventaire.getRace().size() < 3  && this.inventaire.getRace().contains(Constantes.SANG_MELE) )
                        || ((Race) c).getRace() == (Constantes.SANG_MELE) ) {
                    if( this.inventaire.compatibiliteRaceObjet(c) )
                        this.inventaire.add(c);
                    else
                        throw new EquiperException("Un objet ne vous permet pas d'équiper cette race.");
                }
                else  
                    throw new EquiperException("Vous ne pouvez pas équiper plus de race.");
            }
            /*
             * La carte est une classe
             */
            else if(c.getClass().getName().equals(Constantes.C_CLASSE)) {
                
                if( this.inventaire.getClasse().isEmpty()
                        || ( this.inventaire.getClasse().size() < 3 && this.inventaire.getClasse().contains(Constantes.SUPER_MUNCHKIN) )
                        || ((Classe) c).getTypeClasse() == (Constantes.SUPER_MUNCHKIN) ) {
                    if( this.inventaire.compatibiliteClasseObjet(c) )
                        this.inventaire.add(c);
                    else
                        throw new EquiperException("Un objet ne vous permet pas d'équiper cette classe.");
                }
                else  
                    throw new EquiperException("Vous ne pouvez pas équiper plus de classe.");
            }
            
        }
        else
            throw new EquiperException("Cette carte ne peut pas être équipée.");
        
    
    }
    
    
    
    /*
     * Utiliser une action d'une carte
     * @param : Carte
     * @return : /
     * @author : Ophéliec
     * @lastChange : Ophélie: action Classe
     */
    public void utiliserAction(Carte c) {
        //Joueur unJoueur représente le joueur sur lequel on va appliquer l'action.
        
        
        //voleur pendant un tour ( vol à la tire)
        if (this.inventaire.getClasse().contains(Constantes.VOLEUR)){
            this.inventaire.getCartesClasse().get(0).getActionClasse().get(0).action1(this, null, null , c);
            
            
        
        }
    }
    
    public void commencerPartie(Joueur j) {
            j.upNiveau(1, Boolean.TRUE);

            for( int i = 0 ; i < Constantes.NB_CARTES_START ; ++i ) {
                j.getMain().add(j.piocher(Constantes.C_TRESOR));
                j.getMain().add(j.piocher(Constantes.C_DONJON));
            }
            j.setEstmort(false);
    }
    
    
    
    
    
    
    /*
     * Vendre une carte
     * @param : Carte
     * @return : /
     * @author : Ophélie
     * @lastChange : Ophélie: action race
     */
    public void vendre(Carte c) {
        int somme = 0;
        //recupere le prix de la carte
        
        //        somme = c.getPrix();
        
        //     somme = c.getPrix();
        
        
        if (this.inventaire.getRace().contains(Constantes.HALFELIN)){
            somme = somme*2;
        
        }
        
        //augmentation des niveaux
        if ( somme >= 1000){
            this.upNiveau(1, Boolean.TRUE);
        }
        else if ( somme >= 2000){
            this.upNiveau(1, Boolean.TRUE);
        }
        else if ( somme >= 3000){
            this.upNiveau(3, Boolean.TRUE);
        }
    
    }
    
    
    /*
     * Fonction déguerpir lors d'un combat
     * @param : Carte
     * @return : /
     * @author : Ophéliec
     * @lastChange : Ophélie: action Classe
     */
    
    public int lancerDe(){
        int de =0;
        de = (int) (Math.floor(Math.random() * 6) + 1);
        return de;
    
    }
    
    public void deguerpir(Carte c) {
        
        this.bonusDeguerpir = 0;
        int de = 0;
        Monstre m = (Monstre) c;
        
        de = lancerDe();
        //si c'est un magicien, alors il peut défausser des cartes pour gagner des points opur déguerpir
        if ( this.inventaire.getClasse().contains(Constantes.MAGICIEN)){
            this.inventaire.getCartesClasse().get(0).getActionClasse().get(0).action(this, m, null, c);
        }
        //si c'est un elfe , alors il à +1 pour déguerpir
        else if ( this.inventaire.getRace().contains(Constantes.ELFE)){
            this.upBonusDeguerpir(1);
        
        }
        de = de + this.bonusDeguerpir;
        
        
        //si le résultat du dé lancé est supérieur ou égale à 5, alors le joueur peut déguerpir
        if ( de >= 5 )
        //on déguerpit
        {
            //attention aux monstres qui impose un incident facheux meme si on arrive à déguerpir
            
        
        }
        else
        // le joueur ne peut pas déguerpir
        {
            //si c'est un halfelin
            if (( this.inventaire.getRace().contains(Constantes.HALFELIN)))
            {
                if (this.getDeguerpir()==0){
                    
                    //choisir une carte à défausser
                    Carte uneCarte = null;
                    int nbCarte = this.main.size();
                    int i = 0;
                    for ( i = 0; i <nbCarte;  i++ )
                    {
                        this.main.get(i);
                    //afficher cette carte .
                    }
                    
                    //défausse une carte
                    this.defausserCarteMain(uneCarte);
                    this.setDeguerpir(1);
                    this.deguerpir(c);
                
                }
            
            }
            
            
            System.out.println(m.getNom());
            for( IncidentFacheux i : c.getIncidentFacheux()) {
                i.provoquer(this);
            }
        }
    
    }
    
    
    
    /*
     * Fonction combattre
     * @param : Carte
     * @return : /
     * @author : Ophélie
     * @lastChange : Ophélie: action Classe
     */
    //@SuppressWarnings("element-type-mismatch")
    public void combattre( Carte c ) {
        if( ! c.getClass().getName().equals(Constantes.C_MONSTRE)) {
        
        }
        
        Monstre m = (Monstre) c;
        
        if( ! this.inventaire.getClasse().isEmpty()) {
            this.inventaire.getCartesClasse().get(0).getActionClasse().get(0).action(this, m, null, c);
        }
        
        // GUERRIER
        // Si le joueur a une classe guerrier il peut
        // alors défausser 3 cartes et gagner 1 point pour chaque carte défaussée
        if ( this.inventaire.getClasse().contains(Constantes.GUERRIER)){
            this.inventaire.getCartesClasse().get(0).getActionClasse().get(0).action(this, m, null, c);
        }
        
        //PRETRE
        if ( this.inventaire.getClasse().contains(Constantes.PRETRE)){
            this.inventaire.getCartesClasse().get(0).getActionClasse().get(0).action1(this, m, null, c);
        
        }
        
        // MAGICIEN
        //si il est magicien, il peut défausser toute ses cartes pour ne pas combattre le monstre
        if (this.inventaire.getClasse().contains(Constantes.MAGICIEN)){
            if ( this.getMain().size() >= 3){
                
                this.inventaire.getCartesClasse().get(0).getActionClasse().get(0).action1(this, m, null, c);
                //on sort de la fonction, le magicien ne gagne pas de niveau
                return;
            }
        
        }
        
        
        /*
         * Système d'aide
         */
        
        /*
         * Calcul de force
         */
        // Niveau du joueur + objets équipés
        int force = this.niveau + this.inventaire.calculerForceInventaire();
        System.out.println("La force totale du joueur est de " + force + " (" + this.niveau + " + " + this.inventaire.calculerForceInventaire() + ")");
        // A gérer : m.getMalusMonstre()
        
        
        /*----------------------------------------------------------------------*/
        //GUERRIER
        /*----------------------------------------------------------------------*/
        // Si le guerrier est ex-aequo avec le monstre, alors il gagne le combat
        if (this.inventaire.getClasse().contains(Constantes.GUERRIER)){
            if( force == m.getNiveau() ) {
                System.out.println("Il peut vaincre le monstre ! A l'assaut !");
                
                // On choppe les trésors
                for( int i = 0 ; i < m.getNbTresors() ; ++i ) {
                    this.main.add(Partie.piocher(Constantes.C_TRESOR));
                }
                System.out.println("Le joueur pioche " + m.getNbTresors() + " cartes trésor.");
                /*
                 * Charité et ou jouer carte
             */
            }
            else {
                System.out.println("Le monstre est trop fort ! Fuyons !");
                this.deguerpir(c);
            }
        }
        
        // S'il poutre le monstre
        if( force > m.getNiveau() ) {
            System.out.println("Il peut vaincre le monstre ! A l'assaut !");
            // Lvl up & possibilité de gagner la partie
            this.upNiveau(m.getNbNiveaux(), true);
            System.out.println("Le joueur monte de " + m.getNbNiveaux()+ " niveaux.");
            // On choppe les trésors
            for( int i = 0 ; i < m.getNbTresors() ; ++i ) {
                this.main.add(Partie.piocher(Constantes.C_TRESOR));
            }
            System.out.println("Le joueur pioche " + m.getNbTresors() + " cartes trésor.");
            /*
             * Charité et ou jouer carte
         */
        }
        else {
            System.out.println("Le monstre est trop fort ! Fuyons !");
            this.deguerpir(c);
        }
        
        
        
        //retablissement de l'attribut à 0
        this.actionUtiliser = 0;
    } //fin combattre
    
    /*
     * Renvoi le résultat d'un combat
     * @param:
     *  - La liste de monstres à combattre
     *  - La liste de joueurs aidant au combat
     * @return: true, le joueur remporte la victoire.
     * @author: Arthur
     * @date: 08/01/12
     * NB : Reprise de la méthode précédente adaptée à l'IHM
     */
    //public Boolean combat( ArrayList<Monstre> listM, ArrayList<Joueur> listJ) {
    // temporairement, on simplifie avec un seul montre
    public Boolean combat( Monstre m) {
        int lvlM = 0, lvlJ = 0;
        //for( Monstre m : listM ) {
        lvlM += m.getNiveau() + m.getBonusCombat();
        //}
        lvlJ += this.getForce();
        /*for ( Joueur j : listJ ) {
         * lvlJ += j.getForce();
         * }*/
        if(this.getForce() >m .getNiveau()
                || (this.getForce() == m.getNiveau()
                && this.getInventaire().getClasse().contains(Constantes.GUERRIER))) {
            return true;
        }
        else
            return false;
    }
    
    public ArrayList<Carte> winCombat(Monstre m) {
        ArrayList<Carte> c = new ArrayList<Carte>();
        
        // Lvl up & possibilité de gagner la partie
        this.upNiveau(m.getNbNiveaux(), true);
        
        // On choppe les trésors
        for( int i = 0 ; i < m.getNbTresors() ; ++i ) {
            c.add(Partie.piocher(Constantes.C_TRESOR));
        
        }
        
        this.main.addAll(c);
        
        return c;
    }    
    
    /*
     * Getter & Setter
     */
    public int getForce() {
        return this.getNiveau() + this.inventaire.calculerForceInventaire();
    }
    public int getNiveau() {
        return niveau;
    }
    
    public void setNiveau(int niveau) {
        this.niveau = niveau;
    }
    
    /*
     * Change le niveau en fonction de val
     * @param :
     *  - val : la valeur positive ou négative à ajouter au niveau actuel
     *  - partieGagnable : cette montée de niveau permet-elle de gagner la partie si niveau 10 dépassé
     * @author : Arthur
     * change: ophélie: mort
     */
    public void upNiveau(int val, Boolean partieGagnable) {
        if( ((this.niveau + val) <= 10) || (((this.niveau + val) >= 10) && partieGagnable) )
        {
            this.niveau += val;
        }
        
        //le joueur est mort
        if (this.niveau < 1)
        {
            this.setEstmort(true);
            MainApplication.JoueurMourir(this);
            // this.mourir();
            
        }
        
        //si le joueur a le niveau 10 il gagne la partie
        if ( this.niveau >= 10 )
        {
            Partie.gagner(this);
            MainApplication.gagnerPartie();
        }
        
        
    }
    
    public String getNom() {
        return nom;
    }
    
    public void setNom(String nom) {
        this.nom = nom;
    }
    
    public int getSexe() {
        if( !this.sexeChange )
            return sexe;
        else {
            switch (sexe) {
                case Constantes.SEX_FEM :
                    return Constantes.SEX_MAS;
                default:
                    return Constantes.SEX_FEM;
            }
        }
    }
    
    
    
    public int getBonusCombat() {
        return bonusCombat;
    }
    
    public void setBonusCombat(int bonusCombat) {
        this.bonusCombat = bonusCombat;
    }
    
    public void upBonusCombat(int i) {
        this.bonusCombat += i;
    }
    
    public void upBonusDeguerpir(int i) {
        this.bonusDeguerpir += i;
    }
    
    public Inventaire getInventaire() {
        return inventaire;
    }
    
    public boolean tropDeCarte() {
        return (this.main.size() > this.getNbMainDispo() );
    }
    
    public int getNbMainDispo() {
        int nain = (this.inventaire.getRace().contains(Constantes.NAIN)) ? 1 : 0;
        return 5 + nain;
    }

    public String toString() {
        return this.nom + ' ' + this.niveau;
    }

    public void defausserClasseAvecObjets(Carte selected) {
        List<Carte> cartes = new ArrayList<Carte>(inventaire.getListeCartes());
        for ( Carte c : cartes ) {
            if( c.getClass().getName().equals(Constantes.C_OBJET)) {
                Objet o = (Objet) c;
                if( o.getClasse() == ((Classe) selected).getTypeClasse() )
                    defausserInventaireForce(c);
            }
        }
    }
    
    public void defausserRaceAvecObjets(Carte selected) {
        List<Carte> cartes = new ArrayList<Carte>(inventaire.getListeCartes());
        for ( Carte c : cartes ) {
                if( c.getClass().getName().equals(Constantes.C_OBJET)) {
                    Objet o = (Objet) c;
                    if( o.getRace() == ((Race) selected).getRace() )
                        defausserInventaireForce(c);
                }
            }
    }

}
